Lua面向对象

🤔


面向对象的特征

  • 1) 封装:指能够把一个实体的信息、功能、响应都装入一个单独的对象中的特性。
  • 2) 继承:继承的方法允许在不改动原程序的基础上对其进行扩充,这样使得原功能得以保存,而新功能也得以扩展。这有利于减少重复编码,提高软件的开发效率。
  • 3) 多态:同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。在运行时,可以通过指向基类的指针,来调用实现派生类中的方法。
  • 4)抽象:抽象是简化复杂的现实问题的途径,它可以为具体问题找到最恰当的类定义,并且可以在最恰当的继承级别解释问题。

Lua面向对象

Lua中最基本的结构是table,所以需要用table来描述对象的属性。Lua中的function可以用来表示方法。那么Lua中的类可以通过table + function模拟出来。
同时在Lua中,table就是一种对象。首先table与对象一样可以拥有状态。其次,table与对象一样拥有一个独立于其值的标识(self)。最后table与对象一样具有独立于创建者和创建地的生命周期。
对象有其自己的操作,在table中:

1
2
3
4
Account = {balance = 0}
function Account.withdraw(v)
Account.balance = Account.balance - v
end

以上代码,创建了一个新函数,并将该函数存入Account对象的withdraw字段中,可以进行以下调用:
Account.withdraw(100.00)
这种函数就是所谓的“方法”。不过在函数中使用全局名称 Account是一个不好的变成习惯。因为这个函数只能针对特定对象工作,并且这个特定对象还必须存储在特定的全局。如果改变了对象的名称,withdraw就不能继续工作:

1
2
3
a = Account
Account = nil
a.withdraw(100.0) --这里会报错

这种做法违反了之前所说对象的特性,即对象拥有独立的生命周期。
我们有一种灵活的方式,即置顶一项操作所做用的“接受者”,因此需要一个额外的参数来表示该接受者。这个参数通常为self或this:

1
2
3
4
5
6
7
function Account.withdraw(self, v)
self.balance = self.balance - v
end

a1 = Account
Account = nil
a1.withdraw(a1, 7)

使用self参数是所有面向对象语言的一个核心,大多数面向对象语言都能对程序员隐藏部分self参数,从而使的程序员不必显式的申明这个参数。在Lua中,只需要使用冒号,则能隐藏该参数。

1
2
3
4
5
6
function Account:withdraw(v)
self.balance = self.balance - v
end

a = Account
a:withdraw(10)


Lua中的类

类可以看作一个创建对象的模具。大多面向对象的语言都提供了类的概念,在这些语言中,每个对象都是某个特定类的实例。Lua中没有类的概念,每个对象只能自定义行为和形态。不过在Lua中可以模拟类,可以参数一些基于原型的语言,例如newtonscript。在这些语言中对象没有类型,而是每个对象都有一个原型,原型也是一种常规的对象,当其他对象遇到一个未知操作时,原型会先查找它。在这种语言中要表示一个类,只需要创建一个专用作其他对象的原型。
Lua中实现原型很简单,如果有两个对象,a和b,要让b作为a的一个原型,只需要如下操作:
setmetatable(a, {__index = b})
之后,a就会在b中查找所有它没有的操作,可以将b称为对象a的类。

在之前的例子中,为了创建更多的Account,可以让这些新的对象可以从Account行为中继承这些操作,具体的做法就是使用__index元方法。可以应用一些优化,无需创建一个额外的table作为元表,而是使用Account table自身作为元表:

1
2
3
4
5
6
7
8
function Account:new(o)
o = o or {} --如果没有提供table,则创建一个
setmetatable(o, self)
self.__index = self
return o
end
a = Account:new{balance = 0}
a : withdraw(7)

下面提供一个完整的实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Shape = {area = 0}
-- 基础类方法 new
function Shape:new (o,side)
o = o or {}
setmetatable(o, self)
self.__index = self
side = side or 0
self.area = side*side;
return o
end
-- 基础类方法 printArea
function Shape:printArea ()
print("面积为 ",self.area)
end
-- 创建对象
myshape = Shape:new(nil,10)
myshape:printArea()


Lua继承

继承是指一个对象直接使用另一对象的属性和方法。可用于扩展基础类的属性和方法。
以下演示了一个简单的继承实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
Shape = {area = 0}
-- 基础类方法 new
function Shape:new (o,side)
o = o or {}
setmetatable(o, self)
self.__index = self
side = side or 0
self.area = side*side;
return o
end
-- 基础类方法 printArea
function Shape:printArea ()
print("面积为 ",self.area)
end

-- 创建对象
myshape = Shape:new(nil,10)
myshape:printArea()

Square = Shape:new()
-- 派生类方法 new
function Square:new (o,side)
o = o or Shape:new(o,side)
setmetatable(o, self)
self.__index = self
return o
end

-- 派生类方法 printArea
function Square:printArea ()
print("正方形面积为 ",self.area)
end

-- 创建对象
mysquare = Square:new(nil,10)
mysquare:printArea()

Rectangle = Shape:new()
-- 派生类方法 new
function Rectangle:new (o,length,breadth)
o = o or Shape:new(o)
setmetatable(o, self)
self.__index = self
self.area = length * breadth
return o
end

-- 派生类方法 printArea
function Rectangle:printArea ()
print("矩形面积为 ",self.area)
end

-- 创建对象
myrectangle = Rectangle:new(nil,10,20)
myrectangle:printArea()

宇 wechat
扫描二维码,订阅微信公众号